/*
 * Copyright (C) 2011 Mette Bank, Rikke Jensen, Kenneth Brodersen, Thomas Pedersen
 * 
 * This file is part of digiPECS.
 * 
 * digiPECS is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * digiPECS is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with digiPECS.  If not, see <http://www.gnu.org/licenses/>.
 */

package sw6.digipecs.widget.user;

import sw6.digipecs.R;
import sw6.digipecs.exceptions.ImageIsLockedException;
import sw6.digipecs.exceptions.NoViewFoundException;
import sw6.digipecs.model.ExtImageInSentenceBoard;
import sw6.digipecs.model.SentenceBoardList;
import sw6.digipecs.widget.AnimationLayout;
import sw6.digipecs.widget.BitmapImageView;
import sw6.digipecs.widget.DragableExtImageView;
import sw6.digipecs.widget.ExtImageView;
import sw6.digipecs.widget.SentenceBoardExtImageAdapter;
import sw6.digipecs.widget.dragdrop.DragLayout;
import sw6.digipecs.widget.dragdrop.DragListener;
import sw6.digipecs.widget.dragdrop.DropTarget;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.Animation.AnimationListener;
import android.view.animation.TranslateAnimation;
import android.widget.FrameLayout;
import android.widget.GridView;
import android.widget.ImageView;

/**
 * The sentence board layout, build as a compound view. Handles the layout
 * of the sentence board and determines how droppings of pictures into the
 * sentence board should be handled. The layout extends LinearLayout as this
 * is needed to hold a gridview. The gridview is used to store the views of the
 * extended images. The class also implements the droptarget interface as this is
 * container where pictures can be dropped into.
 * @author sw6.digipecs
 *
 */
public class SentenceBoardLayout extends FrameLayout implements DropTarget, DragListener {
	//Constant to set the numbers of pictures in the sentence board
	final static int NUMBERS_OF_PICTURES = 4;
	
	//GridView,  image-list and image adapter belonging to sentence-board layout
	private GridView mSentenceGridview;
	private SentenceBoardList mImagelist;	
	private SentenceBoardExtImageAdapter mAdapter;
	private ImageView mTrashCan;
	
	private final Rect mRect = new Rect();

	private Context mContext;

	// Trying some caching here...
	private Drawable mOpen;
	private Drawable mClose;

	private AnimationLayout mAnimationLayout;
	
	/**
	 * Constructor
	 * @param context - the context to work within
	 */
	public SentenceBoardLayout(Context context) {
		super(context);
		initialise(context);	    
	}	
	
	/**
	 * Constructor - this constructor is need for the view to be inflated properly.
	 * @param context - the context to work within
	 * @param attrs - the collection of attributes, associated tags in XML document. 
	 */
	public SentenceBoardLayout(Context context, AttributeSet attrs) {   
	    super(context, attrs);
	    initialise(context);
	}
	/*
	 *  Initialise the sentence board layout - using a separate 
	 *  method since there are two constructors. Will also implement 
	 *  a onTouchListener on the gridview of the sentence board.
	 */ 
	private void initialise(Context context){
		//inflate mainActivity with the sentence-board layout
		LayoutInflater inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
	    if(inflater != null){       
	    	inflater.inflate(R.layout.sentenceboardlayout, this);
	    }
	    
	    mContext = context;
	    
	    //set setting for the GridView and initialise the adapter &db for the GridView
	    mSentenceGridview = (GridView) findViewById(R.id.sentenceBoardGridView);
	    mSentenceGridview.setNumColumns(NUMBERS_OF_PICTURES);
	    mImagelist = new SentenceBoardList(context, NUMBERS_OF_PICTURES);
	    
	    mTrashCan = (ImageView) findViewById(R.id.sentenceBoardTrashCan);
	    
	    mOpen = getResources().getDrawable(R.drawable.open_bin);
		mClose = getResources().getDrawable(R.drawable.closed_bin);
	}
	
	public void setDragLayer(DragLayout dl) {
		mAdapter = new SentenceBoardExtImageAdapter(mContext, mImagelist.getmImagesInBoard(), dl);
		mSentenceGridview.setAdapter(mAdapter);
		dl.addListener(this);
	}
	
	public void setAnimationLayer(AnimationLayout al) {
		mAnimationLayout = al;
	}

	// TODO Mangler kommentar
	private int findViewIndex(int x, int y) throws NoViewFoundException {
		final int count = mSentenceGridview.getChildCount();
		final Rect rect = mRect;
		
		// Check each view in sentence board, to determine location of coordinates
		for(int i = 0; i < count; i++){
			View child = mSentenceGridview.getChildAt(i);
			child.getHitRect(rect);
			if(rect.contains(x, y)){
				return i;
			}
		}
		
		// No view found
		throw new NoViewFoundException();
	}

	private int findViewIndex(View view) {
		int count = mSentenceGridview.getChildCount();
		ViewGroup parent = mSentenceGridview;
		
		for (int i = 0; i < count; i++) {
			View v = parent.getChildAt(i);
			
			while (v != null && v != view && v instanceof ViewGroup) v = ((ViewGroup) v).getChildAt(0);
			
			if (v == view) {
				return i;
			}
		}
		
		return -1;
	}
	
	/**
	 * Determines how the sentence boards handles a picture dropping. Method implementation
	 * specified by the drop target interface.
	 * @param view - has to be an instance of dragableExtImageView if it is to be handled here.
	 * @param x //TODO Rikke: Mangler kommentar
	 * @param y
	 */
	@Override
	public boolean drop(View view, int x, int y) {
		if(view instanceof DragableExtImageView){
			final Rect rect = mRect;
			
			if (mTrashCan.getVisibility() == VISIBLE) {
				mTrashCan.getHitRect(rect);
				if (rect.contains(x, y)) {
					try {
						dropInTrash(view);
						view.setVisibility(VISIBLE);
						return true;
					} catch (IndexOutOfBoundsException e) { }
				}
			}
			
			mSentenceGridview.getHitRect(rect);
			if (rect.contains(x, y)) {
				try {
					x = x - mSentenceGridview.getLeft();
					y = y - mSentenceGridview.getTop();
					dropInGrid(view, x, y);
					view.setVisibility(VISIBLE);
					return true;
				} catch (NoViewFoundException e) {
				} catch (ImageIsLockedException e) {
					// TODO Auto-generated catch block
				}
				
			}
		}
		
		return false;
	}
	
	private void dropInTrash(View view) throws IndexOutOfBoundsException {
		final int index = findViewIndex(view);
		mImagelist.clearPicture(index);
		mAdapter.notifyDataSetChanged();
	}

	private void dropInGrid(View fromView, int x, int y) throws NoViewFoundException, ImageIsLockedException {
		final int toIndex = findViewIndex(x, y);
		final int fromIndex = findViewIndex(fromView);
		
		final ViewGroup vg = (ViewGroup) mSentenceGridview.getChildAt(toIndex);
		final ExtImageView extView = (ExtImageView) vg.getChildAt(0);
		final ExtImageInSentenceBoard extImage = (ExtImageInSentenceBoard) extView.getImage();
		if (extImage.isLocked()) {
			throw new ImageIsLockedException();
		}
		
		// Determine if the drag originated from the sentence board
		if (fromIndex >= 0) {
			mImagelist.clearPicture(fromIndex);
			mAdapter.notifyDataSetChanged();
		}
		
		// If no animation layout has been set, then we do not do anything
		//TODO Shouldn't they be moved anyways ? (just without animation) 
		if (mAnimationLayout == null) return;
		
		// If dragged within the sentence board, animate the switch too
		if (fromIndex != -1) {
			runAnimation(toIndex, fromIndex);
			mImagelist.clearPicture(toIndex);
			mAdapter.notifyDataSetChanged();
		}
		
		// Animate the drop
		x = x - fromView.getWidth()/2;
		y = y - fromView.getHeight()/2;
		runAnimation(x, y, toIndex, fromView);
	}
	
	private void runAnimation(int fromIndex, int toIndex) {
		View view = mSentenceGridview.getChildAt(fromIndex);
		
		// Find from coordinates
		int fromX = view.getLeft();
		int fromY = view.getTop();
		
		// Find the actual view to move
		if (view instanceof ViewGroup) {
			final ViewGroup vg = (ViewGroup) view;
			view = vg.getChildAt(0);
			
			// Adjust coordinates...
			fromX += view.getLeft();
			fromY += view.getTop();
		}
		
		//TODO Coordinates might be wrong -- Need to coordinate with the other animation...
		runAnimation(fromX, fromY, toIndex, view);
	}
	
	private void runAnimation(final int fromX, final int fromY, final int toIndex, final View view) {		
		// Create Bitmap
		view.buildDrawingCache();
		final Bitmap bitmap = Bitmap.createBitmap(view.getDrawingCache());
		final BitmapImageView biv = new BitmapImageView(mContext, bitmap);
		
		// Create listener, which will actually do the switch
		AnimationListener al = new AnimationListener() {
			@Override
			public void onAnimationStart(Animation animation) { }
			@Override
			public void onAnimationRepeat(Animation animation) { }
			@Override
			public void onAnimationEnd(Animation animation) {
				if (view instanceof ExtImageView) {
					final ExtImageView extView = (ExtImageView) view;
					mImagelist.setPicture(toIndex, extView.getImage());
					mAdapter.notifyDataSetChanged();
				}
			}
		};
		
		// Calculate offset accordingly (We assume that animationlayout extends the full screen.
		ViewGroup vg = mSentenceGridview;
		int offsetX = 0;
		int offsetY = 0;
		while (vg != null) {
			offsetX += vg.getLeft();
			offsetY += vg.getTop();
			
			ViewParent vp = vg.getParent();
			while (vp != null && !(vp instanceof ViewGroup)) vp = vp.getParent();
			if (vp == null) break;
			vg = (ViewGroup) vp;
		}
		
		// Find the to-coordinates
		int toX, toY;
		View toView = mSentenceGridview.getChildAt(toIndex);
		toX = toView.getLeft();
		toY = toView.getTop();
		if (toView instanceof ViewGroup) {
			final ViewGroup toViewGroup = (ViewGroup) toView;
			toView = toViewGroup.getChildAt(0);
			toX += toView.getLeft();
			toY += toView.getTop();
		}
		
		// Create the animation
		TranslateAnimation animation = new TranslateAnimation(fromX + offsetX, toX + offsetX, fromY + offsetY, toY + offsetY);
		animation.setDuration(250);
		animation.setAnimationListener(al);
		
		// Start the animation
		mAnimationLayout.startAnimation(biv, animation, al);
	}

	/**
	 * Will change the background colour of the sentence board
	 * the moment a dragable image is found in the same coordinates
	 * as the sentence board.
	 * @see sw6.digipecs.widget.dragdrop.DropTarget#onDragIn()
	 */
	@Override
	public void onDragIn(int x, int y){
		final Rect rect = mRect;
		
		if (mTrashCan.getVisibility() != INVISIBLE) {
			mTrashCan.getHitRect(rect);
			if (rect.contains(x, y)) {
				mTrashCan.setImageDrawable(mOpen);
			} else {
				mTrashCan.setImageDrawable(mClose);
			}
		}
		
		mSentenceGridview.getHitRect(rect);
		if (rect.contains(x, y)) {
			mSentenceGridview.setBackgroundResource(R.color.sb_background_over);
		} else {
			mSentenceGridview.setBackgroundResource(R.color.sb_background_drag);
		}
	}
	/**
	 * Will change the background colour of the sentence board
	 * the moment no drageble image is found in the same coordinates
	 * as the sentence board
	 * @see sw6.digipecs.widget.dragdrop.DropTarget#onDragOut()
	 */
	@Override
	public void onDragOut(){
		mSentenceGridview.setBackgroundResource(R.color.sb_background_drag);
		mTrashCan.setImageDrawable(mClose);
	}
	
	// TODO Update comment
	/**
	 * Will retrieve data associated with the sentence board that was
	 * saved in the last session of the activity associated with the
	 *  sentence board.
	 * @see android.view.View#onRestoreInstanceState(android.os.Parcelable)
	 */
	public void restoreState() {
		mImagelist.restoreState();
		mAdapter.notifyDataSetChanged();
	}
	
	// TODO Update comment
	/**
	 * Will insure that data associated with the sentence board will
	 * become persistent data by saving this data to a database.
	 * @see android.view.View#onSaveInstanceState()
	 */
	public void saveState() {
		mImagelist.saveState();
	}

	@Override
	public void onDragStop() {
		if (mTrashCan.getVisibility() != INVISIBLE) {
			AlphaAnimation animation = new AlphaAnimation(1.0F, 0.0F);
			animation.setDuration(1000);
			mTrashCan.startAnimation(animation);
			mTrashCan.setVisibility(INVISIBLE);
		}
		
		mSentenceGridview.setBackgroundResource(R.color.sb_background);
	}

	@Override
	public void onDragStart(View v) {
		ViewParent parent = v.getParent();
		while (parent != null && parent != mSentenceGridview) parent = parent.getParent();
		
		if (parent == mSentenceGridview) {
			AlphaAnimation animation = new AlphaAnimation(0.0F, 1.0F);
			animation.setDuration(250);
			mTrashCan.startAnimation(animation);
			mTrashCan.setVisibility(VISIBLE);
			mTrashCan.setImageResource(R.drawable.closed_bin);
		}
		
		mSentenceGridview.setBackgroundResource(R.color.sb_background_drag);
	}
	
	public void clearState() {
		mImagelist.clearState();
	}
}
